#include "SudokuMainWindow.h"
#include "ui_SudokuMainWindow.h"

#include <QMessageBox>
#include <QFileDialog>
#include <QPrintDialog>
#include <QKeyEvent>
#include <QLineEdit>
#include <QLabel>
#include <QClipboard>
#include <QPrinter>
#include <QPainter>
#include <QSettings>

/**
  * Dynamic property name for check box and line edit
  */
static const char* MASK_PROPERTY_NAME = "Mask_Property_Name";

SudokuMainWindow::SudokuMainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::SudokuMainWindow),
    mModel(new SudokuDataModel(new SudokuData(), this)),
    mSolveMask(Unknown)
{
    ui->setupUi(this);
    ui->sudokuDiagram->setModel(mModel);

    bool ConnectOk = connect(mModel, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)),
            this, SLOT(updateView(const QModelIndex&, const QModelIndex&)));

    Q_ASSERT(ConnectOk);

    QString styleString("");
    QFile styleFile(":/resource/stylesheet/SudokuStylesheet.qss");
    if (styleFile.open (QIODevice::ReadOnly | QIODevice::Text))
    {
        styleString = QString(styleFile.readAll());
        styleFile.close();
    }

    setStyleSheet(styleString);

    foreach(SudokuAbstractSolvingTechnique* technique, mSolver.getMap())
    {
        QLabel* label = new QLabel(technique->getName(), this);
        QLineEdit* lineEdit = new QLineEdit(this);
        lineEdit->setProperty(MASK_PROPERTY_NAME, static_cast<int>(technique->getType()));
        mLineEditList.append(lineEdit);
        ui->solveMaskLayout->addRow(label, lineEdit);

        QAction* techniqueAction = new QAction(technique->getName(), this);
        techniqueAction->setCheckable(true);
        techniqueAction->setData(technique->getType());

        ConnectOk = connect(techniqueAction, SIGNAL(toggled(bool)), this, SLOT(updateMask(bool)));
        Q_ASSERT(ConnectOk);

        //if(technique->getCategory() == FindValue)
        //{
            techniqueAction->setChecked(true);
        //}
        ui->menuTechniques->addAction(techniqueAction);
    }

    readSettings();
}

SudokuMainWindow::~SudokuMainWindow()
{
    delete ui;
}

void SudokuMainWindow::solveDiagram()
{
    qDebug() << "SudokuMainWindow::solveDiagram";
    SudokuData* sudokuData = mModel->getSudokuData();
    if(!sudokuData)
    {
        qDebug() << "SudokuMainWindow::solveDiagram no data to solve";
        return;
    }

    qDebug() << "SudokuMainWindow::solveDiagram solveMask " << mSolveMask;

    //TODO check on beginning
    mSolver.solve(sudokuData, mSolveMask);
    qDebug() << "filled: " << sudokuData->isFilled();
    qDebug() << "solve: " << sudokuData->isSolved();
    qDebug() << "isSolvable: " << sudokuData->isSolvable();
    if(sudokuData->isSolved())
    {
        ui->statusbar->showMessage(tr("Solved"), 5000);
    }
    else if(sudokuData->isSolvable())
    {
        ui->statusbar->showMessage(tr("Not solved, valid"), 5000);
    }
    else
    {
        QMessageBox::critical(this, "not solved", "invalid diagram");
    }
    //TODO  remove upper
    if(!sudokuData->isValid())
    {
        QMessageBox::critical(this, "NOT VALID", "NOT VALID");
    }

    // Fill result line edits depending on mask property
    for(int i=0; i<mLineEditList.count(); ++i)
    {
        QLineEdit* lineEdit = mLineEditList.at(i);
        lineEdit->clear();
        QVariant mask = lineEdit->property(MASK_PROPERTY_NAME);
        if(mask.isValid())
        {
            bool ok = false;
            int maskValue = mask.toInt(&ok);
            if(ok && (mSolveMask & maskValue))
            {
                CellFillerTypes type = static_cast<CellFillerTypes>(maskValue);
                int count = mSolver.getTechniqueSolveCount(type);

                qDebug() << "SudokuMainWindow::solveDiagram count=" << count;

                // Display amount of values found in current run
                lineEdit->setText(QString::number(count));
            }
        }
    }
}

void SudokuMainWindow::loadFromFile()
{
    QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"),
                                                     "./",
                                                     tr("Sudoku files (*.sss *.sud *.sdk *.s *.ss)"));
    qDebug() << "SudokuMainWindow::loadFromFile fileName: " << fileName;

    if(fileName.isEmpty())
        return;


    SudokuData* sudokuData = new SudokuData("",fileName);
    //TODO where to delete? smart pointer?
    if(!sudokuData->isSolvable())
    {
        QMessageBox::critical(this, "Bad sudoku", "Bad sudoku");
    }
    else
    {
        ;//QMessageBox::information(this, "ok", "ok");
    }


    mModel->setSudokuData(sudokuData);

    qDebug() << *sudokuData;
    qDebug() << "possibleSolutions: " << sudokuData->countPossibleSolutions();

    ui->statusbar->showMessage(tr("File loaded"), 5000);

    //TODO call for all cells?
    //ui->sudokuDiagram->openPersistentEditor(mModel->index(0,1));
}

void SudokuMainWindow::saveToFile()
{
    QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"),
                               "./sudoku.sss",
                               tr("sss (*.sss)"));

    qDebug() << "SudokuMainWindow::saveToFile fileName: " << fileName;

    if(fileName.isEmpty())
        return;

    mModel->getSudokuData()->saveToFile("", fileName);
    ui->statusbar->showMessage(tr("File saved"), 5000);
}

void SudokuMainWindow::clearDiagram()
{
    //TODO connect directly button to model
    mModel->clear();
}

void SudokuMainWindow::paste()
{
    qDebug() << "SudokuMainWindow::paste";
    QClipboard* clipboard = QApplication::clipboard();
    QString clipboardText = clipboard->text();

    if(clipboardText.isEmpty())
    {
        ui->statusbar->showMessage(tr("Not valid sudoku data"), 5000);
    }
    else
    {
        //TODO SudokuFactory class to create valid sudoku or return null
        SudokuData* sudokuData = new SudokuData(clipboardText);
        if(!sudokuData->isSolvable())
        {
            QMessageBox::critical(this, "Bad sudoku", "Bad sudoku");
            delete sudokuData;
            sudokuData = 0;
        }
        else
        {
            mModel->setSudokuData(sudokuData);

            qDebug() << *sudokuData;
            qDebug() << "possibleSolutions: " << sudokuData->countPossibleSolutions();

            ui->statusbar->showMessage(tr("Paste success"), 5000);
        }
    }
}

void SudokuMainWindow::print()
{
    //TODO improve it!!!!
    qDebug() << "SudokuMainWindow::print";
    // TODO printer as class member
    QPrinter printer;//(QPrinter::HighResolution);
    printer.setOutputFileName("diagram.pdf");

    qDebug() << "SudokuMainWindow::print resolution=" << printer.resolution();
    //setResolution()  ?

    //QPainter painter(&printer);

    //QPixmap diagramPixmap = QPixmap::grabWidget(ui->sudokuDiagram);
    //painter.drawPixmap(0, 0, diagramPixmap);

    ui->sudokuDiagram->render(&printer);//, QPoint(), QRegion(0, 0, 2000, 2000));

    /*    QPrintDialog printDialog(&printer, this);
    if (printDialog.exec())
    {
        QPainter painter(&printer);
        //QRect rect = painter.viewport();
        //QSize size = image.size();
        //size.scale(rect.size(), Qt::KeepAspectRatio);
        //painter.setViewport(rect.x(), rect.y(),
        //size.width(), size.height());
        //painter.setWindow(image.rect());
        //painter.drawImage(0, 0, image);

        QPixmap diagramPixmap = QPixmap::grabWidget(ui->sudokuDiagram);
        painter.drawPixmap(0, 0, diagramPixmap);
    }*/

/*
    // redirect table's painting on a pixmap
    QPixmap pixmap(ui->sudokuDiagram->width(), ui->sudokuDiagram->height());
    QPainter::setRedirected(ui->sudokuDiagram->viewport(), &pixmap);
    QPaintEvent event(QRect(0, 0, ui->sudokuDiagram->width(), ui->sudokuDiagram->height()));
    QApplication::sendEvent(ui->sudokuDiagram->viewport(), &event);
    QPainter::restoreRedirected(ui->sudokuDiagram->viewport());

    painter.drawPixmap(0, 0, pixmap);*/
}

void SudokuMainWindow::updateView(const QModelIndex& topLeft, const QModelIndex& bottomRight)
{
    Q_UNUSED(bottomRight);
    qDebug() << "SudokuMainWindow::updateView col " << topLeft.column() << " row " << topLeft.row();
    //TODO update view more precisely - update only one cell?
    /*if(topLeft == bottomRight)
        ui->sudokuDiagram->update(topLeft);
    else
        qDebug() << "SudokuMainWindow::updateView area - NOT updating";*/
    //need to update all as solving is in the same thread, check again when threads implemented

    //ui->sudokuDiagram->repaint();
}

void SudokuMainWindow::updateMask(bool Add)
{
    QObject* senderObject = sender();
    if(senderObject)
    {
        QAction* senderAction = qobject_cast<QAction*>(senderObject);
        if(senderAction)
        {
            bool ok = false;
            int methodType = senderAction->data().toInt(&ok);
            if(ok)
            {
                CellFillerTypes type = static_cast<CellFillerTypes>(methodType);
                if(Add)
                {
                    //TODO use Q_FLAGS
                    mSolveMask = static_cast<CellFillerTypes>(mSolveMask | type);
                }
                else
                {
                    //TODO use Q_FLAGS
                    mSolveMask = static_cast<CellFillerTypes>(mSolveMask & (~type));
                }
                qDebug() << "SudokuMainWindow::updateMask Add=" << Add << " type=" << type << " mSolveMask=" << mSolveMask;
            }
        }
    }
}

void SudokuMainWindow::changeEvent(QEvent *e)
{
    QMainWindow::changeEvent(e);
    switch (e->type()) {
    case QEvent::LanguageChange:
        ui->retranslateUi(this);
        break;
    default:
        break;
    }
}

void SudokuMainWindow::closeEvent(QCloseEvent* event)
{
    if(reallyExit())
    {
        saveSettings();
        event->accept();
    }
    else
    {
        event->ignore();
    }
}

void SudokuMainWindow::readSettings()
{
    //TODO company and app name
    QSettings settings("Home", "Sudoku");
    int testSettings = settings.value("test", 0).toInt();

    qDebug() << "SudokuMainWindow::readSettings testSettings=" << testSettings;
}

void SudokuMainWindow::saveSettings()
{
    //TODO company and app name
    QSettings settings("Home", "Sudoku");
    settings.setValue("test", 1);
}

bool SudokuMainWindow::reallyExit()
{
    //TODO implement
    return true;
}

/*void SudokuMainWindow::keyPressEvent (QKeyEvent* event)
{
    if(event->key() == Qt::Key_Paste)
        qDebug() << "paste";

}*/
